#include "genomeelement.h"
#include "contig.h"

GenomeElement::GenomeElement(QPointer<SageVis> sageVisPointer, MultiGeneRegionVisWidget* parentWidget, Contig* parent ) :
    VisBaseElement( sageVisPointer,  dynamic_cast<VisBaseWidget*> (parentWidget), dynamic_cast<QGraphicsItem*> (parent) )
{
    init();
    parentContig = parent;
    uniqueId = "";
}

GenomeElement::GenomeElement(QPointer<GenomeElementDataPacket> dataPacket , QPointer<SageVis> sageVisPointer, MultiGeneRegionVisWidget* parentWidget, Contig* parent ) :
    VisBaseElement( sageVisPointer, dynamic_cast<VisBaseWidget*> (parentWidget),  dynamic_cast<QGraphicsItem*> (parent))
{
    init();
    setDataPacket( dataPacket );
    setAcceptHoverEvents(true);
    QString type = dataPacket->getType();
    type.toLower();
    if( type.contains("cds" ) || type.contains("CDS") )//type.contains("gene") || type.contains("cds") || type.contains("rna") )
        setVisible( true );
    else
        setVisible( false );

    parentContig = parent;
    uniqueId = dataPacket->getUniqueId();//getSeqId();

    //setCacheMode( QGraphicsItem :: DeviceCoordinateCache, boundingRect().size().toSize() );
    setCacheMode(QGraphicsItem::ItemCoordinateCache);//this call is magic- render to pixmap makes it faster
}

void GenomeElement::init()
{
    //-------HEIGHT AND POSITION INIT-----
    setHeight(parentWidget->geometry().height()*.95);
    setPos(0,0);

    //-------SET DEFAULT COLORS--------
    defaultColor.setRgb( 147.0, 215.0, 255.0);//, 150.0 );
    givenColor.setRgb( 147.0, 215.0, 255.0);//, 150.0 );//same at start as default color
    highlightColor.setRgb( 255.0, 242.0, 42.0);//, 150.0 );

    //------INIT VALUES FOR DRAWING VARIABLES-----
    drawArrow = false;
    drawForward = false;

    //------DRAW CHILDREN (INFOBOX) OUTSIDE BOUNDING RECT----
    setFlag(QGraphicsItem::ItemClipsChildrenToShape, false);

    acceptAlignmentSignals = false;
    emitAlignmentSignal = true;

    initShape();
}


void GenomeElement::initShape()
{
    //----setup draw----
    //QRectF bRect = boundingRect();
    bRect = boundingRect();
    if( drawForward )
    {
        float triangleDiff =  -bRect.width()*.25;//how far to draw rect before hitting triangle
        if( bRect.width()*.25 < 4 )
            triangleDiff = -(bRect.width());//if small gene- just draw triangle
        if( bRect.width() > 12 )
            triangleDiff = -12.0;
//        bRect.adjust(0, .25*bRect.height(), triangleDiff, -.25*bRect.height()); //adjust bounding rect
        //painter->drawRect(bRect);//draw

        //draw triangle
        bRect = boundingRect();
//        QPointF p1( bRect.x()+bRect.width(), bRect.y()+bRect.height()/2 );
//        QPointF p2( bRect.x()+bRect.width()+triangleDiff, bRect.y());
//        QPointF p3( bRect.x()+bRect.width()+triangleDiff, bRect.y()+bRect.height() );
//       QPolygonF poly;
        p1.setX(bRect.x()+bRect.width());
        p1.setY(bRect.y()+bRect.height()/2);
        p2.setX(bRect.x()+bRect.width()+triangleDiff);
        p2.setY(bRect.y());
        p3.setX(bRect.x()+bRect.width()+triangleDiff);
        p3.setY(bRect.y()+bRect.height());
        QPolygonF temp;
        temp << p1 << p2 << p3;
        poly.swap(temp);
        //painter->drawPolygon(poly);

        //setup rectangle to draw
        bRect.adjust(0, .25*bRect.height(), triangleDiff, -.25*bRect.height()); //adjust bounding rect
    }
    else //draw arrow to the left
    {
        float triangleDiff =  bRect.width()*.25;
        if( bRect.width()*.25 < 4 )
            triangleDiff = (bRect.width());
        if( bRect.width() > 12 )
            triangleDiff = 12.0;

        //bRect.adjust(triangleDiff, .25*bRect.height(), 0, -.25*bRect.height());
        //painter->drawRect(bRect);

        bRect = boundingRect();
//        QPointF p1( bRect.x(), bRect.y()+bRect.height()/2 );
//        QPointF p2( bRect.x()+triangleDiff, bRect.y());
//        QPointF p3( bRect.x()+triangleDiff, bRect.y()+bRect.height() );
//        QPolygonF poly;
        p1.setX(bRect.x());
        p1.setY(bRect.y()+bRect.height()/2);
        p2.setX(bRect.x()+triangleDiff);
        p2.setY(bRect.y());
        p3.setX(bRect.x()+triangleDiff);
        p3.setY(bRect.y()+bRect.height());
        QPolygonF temp;
        temp << p1 << p2 << p3;
        poly.swap(temp);
        //painter->drawPolygon(poly);

        bRect.adjust(triangleDiff, .25*bRect.height(), 0, -.25*bRect.height());

    }
}

void GenomeElement::setGeometry(const QRectF &geom)
{
    super::setGeometry(geom);
    initShape();
}

void GenomeElement::setGeometry( qreal x, qreal y, qreal w, qreal h )
{
    super::setGeometry(x, y, w, h);
    initShape();
}

void GenomeElement::setWidth(float w)
{
    super::setWidth(w);
    initShape();
}

void GenomeElement::setHeight(float h)
{
    super::setHeight(h);
    initShape();
}

void GenomeElement::setDataPacket(QPointer<GenomeElementDataPacket> dataPacket)
{
    this->dataPacket = dataPacket;
    uniqueId = dataPacket->getSeqId();

    //SETUP draw based on data packet values
    QString type = dataPacket->getType().toLower();
    drawArrow = type.contains("gene")|| type.contains("cds") || type.contains("rna") ; //draw arrow if gene, cds or rna
    drawForward = dataPacket->getPosStrand(); //( isPosStrand && parent->minNT < parent->maxNT ) || ( !isPosStrand && parent->minNT > parent->maxNT );
}

int GenomeElement::getStart()
{
    return dataPacket->getStart();
}

int GenomeElement::getStop()
{
    return dataPacket->getStop();
}

QString GenomeElement::getClusterId()
{
    return dataPacket->getClusterId();
}

QColor GenomeElement::getColor()
{
    return givenColor;
}

QPointer< Contig > GenomeElement::getParentContig()
{
    return parentContig;
}

QString GenomeElement::getUniqueId()
{
    return uniqueId;
}

void GenomeElement::setIdx(int i)
{
    idx = i;
}

int GenomeElement::getIdx()
{
    return idx;
}

void GenomeElement::setColor( QColor newColor )
{
    givenColor = newColor;
    //emit( colorChangedSignal(newColor) );
    emit( signalingColorChange(newColor, dataPacket->getClusterId()));

}

void GenomeElement::setColor( QColor newColor, bool emitSignal )
{
    givenColor = newColor;
    if( emitSignal )
    {
        emit( signalingColorChange(newColor, dataPacket->getClusterId()));
        //emit( colorChangedSignal(newColor) );
    }
}

void GenomeElement::setHighlightColor()
{
    setColor(highlightColor, true);
}

void GenomeElement::setDrawForward(bool df)
{
    drawForward = df;
}

bool GenomeElement::getDrawForward()
{
    return drawForward;
}

QString GenomeElement::getIdentifierToDisplay()
{
    return dataPacket->getIdentifierToDisplay();
}

void GenomeElement::processColorChangeEvent(QColor newColor)
{
    super::processColorChangeEvent(newColor);
    givenColor = newColor;
}

void GenomeElement::processHoverEvent(QGraphicsSceneHoverEvent *event)
{
    //qDebug() << " hovering over a genome element!!  " << endl;

    super::processHoverEvent(event); //call base class to ensure hover event goes through


}

void GenomeElement::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
    //emit( showInfoBoxSignal(dataPacket, this )) ;
    if( infoBox == NULL )
    {
        if( mapToItem(parentWidget, QPointF(0.0, 0.0)).y() > 300.0 )
        {
            infoBox = new InfoBox( this, dataPacket, 0, sageVisPointer, parentWidget, this);//parentWidget);//
            infoBox->forgeConnection(this, HOVER_CONNECTION );
            //QPointF newPt = mapToItem(parentWidget, 500.0, 500.0);//event->pos().x(), event->pos().y()-300.0 );
            infoBox->setGeometry(event->pos().x(), -300.0, 400.0, 300.0);//newPt.x(), newPt.y(), 400.0, 300.0);//event->pos().x(), -300.0, 400.0, 300.0);
//        infoBox->setZValue(1000000);
            parentItem()->setZValue(100000000000);
        }
        else
        {
            infoBox = new InfoBox( this, dataPacket, 1, sageVisPointer, parentWidget, this);//parentWidget);//
            infoBox->forgeConnection(this, HOVER_CONNECTION );
            //QPointF newPt = mapToItem(parentWidget, 500.0, 500.0);//event->pos().x(), event->pos().y()-300.0 );
            infoBox->setGeometry(event->pos().x(), 0.0, 400.0, 300.0);//newPt.x(), newPt.y(), 400.0, 300.0);//event->pos().x(), -300.0, 400.0, 300.0);
    //        infoBox->setZValue(1000000);
            parentItem()->setZValue(100000000000);

            qDebug() << "draw down " << endl;
        }
    }

    super::processHoverEvent(event);

    //test
    qDebug() << "signalling hover start " << endl;
    emit( signalingHover(true, event, dataPacket->getClusterId() ) );

    //super::hoverEnterEvent(event);
}

void GenomeElement::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
{
    if( infoBox == NULL )
    {
        if( mapToItem(parentWidget, QPointF(0.0, 0.0)).y() > 300.0 )
        {
            infoBox = new InfoBox( this, dataPacket, 0, sageVisPointer, parentWidget, this);//parentWidget);//
            infoBox->forgeConnection(this, HOVER_CONNECTION );
            //QPointF newPt = mapToItem(parentWidget, 500.0, 500.0);//event->pos().x(), event->pos().y()-300.0 );
            infoBox->setGeometry(event->pos().x(), -300.0, 400.0, 300.0);//newPt.x(), newPt.y(), 400.0, 300.0);//event->pos().x(), -300.0, 400.0, 300.0);
//        infoBox->setZValue(1000000);
            parentItem()->setZValue(100000000000);
        }
        else
        {
            infoBox = new InfoBox( this, dataPacket, 1, sageVisPointer, parentWidget, this);//parentWidget);//
            infoBox->forgeConnection(this, HOVER_CONNECTION );
            //QPointF newPt = mapToItem(parentWidget, 500.0, 500.0);//event->pos().x(), event->pos().y()-300.0 );
            infoBox->setGeometry(event->pos().x(), 0.0, 400.0, 300.0);//newPt.x(), newPt.y(), 400.0, 300.0);//event->pos().x(), -300.0, 400.0, 300.0);
    //        infoBox->setZValue(1000000);
            parentItem()->setZValue(100000000000);
        }
    }
    else
    {
        if( mapToItem(parentWidget, QPointF(0.0, 0.0)).y() > 300.0 )
            infoBox->setGeometry(event->pos().x(), -300.0, 400.0, 300.0);//setPos(event->pos().x(), -300.0 );//
        else
            infoBox->setGeometry(event->pos().x(), this->boundingRect().height(), 400.0, 300.0);//newPt.x(), newPt.y(), 400.0, 300.0);//event->pos().x(), -300.0, 400.0, 300.0);
    }
}

void GenomeElement::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{ 
    //emit( removeInfoBoxSignal(dataPacket, this )) ;
    if( infoBox != NULL )
    {
        parentItem()->setZValue(0);
        if( ! infoBox->geometry().contains( event->pos() ) )
            delete infoBox;
    }

    //super::hoverLeaveEvent(event);
    super::processHoverEvent(event);

    emit( signalingHover(false, event, dataPacket->getClusterId() ) );

}

void GenomeElement::forgeConnection(QPointer<VisBaseElement> elt, int type)
{
    //qDebug() << "genome elt forge connection" << endl;
    GenomeElement* e = dynamic_cast<GenomeElement*>(elt.data() );
    if( type == ALIGNMENT_CONNECTION )//&& e->getParentContig()->getContigId().compare( this->getParentContig()->getContigId()) != 0 )
    {
        acceptAlignmentSignals = true;
        connect( this, SIGNAL(alignmentSignal(float,bool,QString)), e, SLOT(alignmentSlot(float,bool,QString)) );
    }
    if( type == CENTER_CONNECTION )
    {
        acceptCenterSignals = true;
        connect( this, SIGNAL(centerSignal()), e, SLOT(centerSlot()) );
    }
    super::forgeConnection(elt, type);
}

//note:  am I aligning myself?
void GenomeElement::emitAlignmentSlot()
{
    qDebug() << "emit alignment signal!" << endl;
    QPointF loc = QPointF(0,0);
    QPointF distToMove = mapToScene( loc );
    //emit( alignmentSignal(distToMove.x(), dataPacket->getPosStrand(), dataPacket->getParentContigId() ) );
    emit( signalingAlignment(distToMove.x(), dataPacket->getPosStrand(), dataPacket->getParentContigId(), getClusterId() ) );
}

void GenomeElement::emitCenteredSlot()
{
    //emit(centerSignal());//center others in cluster
    emit( signalingCenter(getClusterId() ) );
    centerSlot();//center myself
}

void GenomeElement::emitMoveToTopSlot()
{
    emit(moveToTopSignal(dataPacket->getClusterId()));
}

void GenomeElement::emitTargetSlot()
{
    //emit(targetSignal(dataPacket->getClusterId()));
    emitMoveToTopSlot();
    emitCenteredSlot();
    emitAlignmentSlot();//so elts flip
    QColor c;
    c.setRgb(255.0, 208.0, 38.0, 255.0); //255.0, 96.0, 75.0, 255.0);
    setColor(c, true);

    //set adjacent colors of elements
    parentContig->targetElement(uniqueId);
}

void GenomeElement::emitTargetSlot(QColor leftColor, QColor rightColor, QColor startColor)
{
    emitMoveToTopSlot();
    emitCenteredSlot();
    emitAlignmentSlot();//so elts flip
    //QColor c;
    //c.setRgb(//255.0, 208.0, 38.0, 255.0); //255.0, 96.0, 75.0, 255.0);
    setColor(startColor, true);//c, true);

    //set adjacent colors of elements
    parentContig->targetElement(uniqueId, leftColor, rightColor, startColor);
}

void GenomeElement::alignmentSlot(float xPos, bool posStrand, QString parentContigId)
{

    if( parentContigId.compare(this->dataPacket->getParentContigId()) == 0)//for now do nothing if in same contig
    {
        qDebug() << "Occurs twice in the same contig " << endl;
        return;
    }

    if( (dataPacket->getPosStrand() != posStrand && !parentContig->getFlipped())
            || ( dataPacket->getPosStrand() == posStrand && parentContig->getFlipped() ) )
    {
        parentContig->flip();
    }

    float deltaX = xPos - mapToScene(0.0, 0.0).x();
    parentContig->moveBy(deltaX);
    parentContig->layoutElementsNonThreaded();
    parentContig->update();

}

void GenomeElement::centerSlot()
{
    MultiGeneRegionVisWidget* p =  dynamic_cast<MultiGeneRegionVisWidget*> (parentWidget.data());
    float targetX = p->getCenterXPosition();

    float deltaX = targetX - mapToItem(parentWidget, QPointF(0.0, 0.0)).x();
    //float deltaNt = -1.0* deltaX * p->getNtPerPixel();
    //parentContig->moveBy(deltaX, deltaNt);
    parentContig->moveBy(deltaX);
    parentContig->layoutElementsNonThreaded();
    parentContig->update();
}

void GenomeElement::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    //----prevent compiler warnings by declaring what I am not using---
    Q_UNUSED(widget);
    Q_UNUSED(option);

    //painter->setClipRect( option->exposedRect );//just draw what is needed to be drawn- doesn't improve performance

    //---set color---
    QColor backgroundColor;

    if( hovering )
    {
        backgroundColor.setRgb(highlightColor.red(), highlightColor.green(), highlightColor.blue(), highlightColor.alpha());
    }
    else
    {
        backgroundColor.setRgb(givenColor.red(), givenColor.green(), givenColor.blue(), givenColor.alpha());


//        //HACK
//        if( dataPacket->getClusterId().compare("Cluster4") == 0 )
//            backgroundColor.setRgb( 219, 100, 126 );
//        if( dataPacket->getClusterId().compare("Cluster31") == 0 )
//            backgroundColor.setRgb( 100,136,219 );
//        if( dataPacket->getClusterId().compare("Cluster36548") == 0 )
//            backgroundColor.setRgb( 100,199,219 );
//        if( dataPacket->getClusterId().compare("Cluster64") == 0 )
//            backgroundColor.setRgb( 100,219,152);
//        if( dataPacket->getClusterId().compare("Cluster147") == 0 )
//            backgroundColor.setRgb( 219,145,100);
//        if( dataPacket->getClusterId().compare("Cluster4595") == 0 )
//            backgroundColor.setRgb( 181, 106, 219);
//        if( dataPacket->getClusterId().compare("Cluster247") == 0 )
//            backgroundColor.setRgb( 107, 80, 219 );
//        if( dataPacket->getClusterId().compare("Cluster16907") == 0 )
//            backgroundColor.setRgb( 219,69,69);
//        if( dataPacket->getClusterId().compare("Cluster1538") == 0 )
//            backgroundColor.setRgb( 56, 152, 48);
//        if( dataPacket->getClusterId().compare("Cluster7835") == 0 )
//            backgroundColor.setRgb( 255, 199, 81 );


    }

    //initShape();

    painter->setBrush(backgroundColor);
    painter->setPen( Qt::NoPen );

    painter->drawRect(bRect);
    painter->drawPolygon(poly);

//    //----setup draw----
//    QRectF bRect = boundingRect();
//    if( drawForward )
//    {
//        float triangleDiff =  -bRect.width()*.25;//how far to draw rect before hitting triangle
//        if( bRect.width()*.25 < 4 )
//            triangleDiff = -(bRect.width());//if small gene- just draw triangle
//        if( bRect.width() > 12 )
//            triangleDiff = -12.0;
//        bRect.adjust(0, .25*bRect.height(), triangleDiff, -.25*bRect.height()); //adjust bounding rect
//        painter->drawRect(bRect);//draw

//        //draw triangle
//        bRect = boundingRect();
//        QPointF p1( bRect.x()+bRect.width(), bRect.y()+bRect.height()/2 );
//        QPointF p2( bRect.x()+bRect.width()+triangleDiff, bRect.y());
//        QPointF p3( bRect.x()+bRect.width()+triangleDiff, bRect.y()+bRect.height() );
//        QPolygonF poly;
//        poly << p1 << p2 << p3;
//        painter->drawPolygon(poly);
//    }
//    else //draw arrow to the left
//    {
//        float triangleDiff =  bRect.width()*.25;
//        if( bRect.width()*.25 < 4 )
//            triangleDiff = (bRect.width());
//        if( bRect.width() > 12 )
//            triangleDiff = 12.0;

//        bRect.adjust(triangleDiff, .25*bRect.height(), 0, -.25*bRect.height());
//        painter->drawRect(bRect);

//        bRect = boundingRect();
//        QPointF p1( bRect.x(), bRect.y()+bRect.height()/2 );
//        QPointF p2( bRect.x()+triangleDiff, bRect.y());
//        QPointF p3( bRect.x()+triangleDiff, bRect.y()+bRect.height() );
//        QPolygonF poly;
//        poly << p1 << p2 << p3;
//        painter->drawPolygon(poly);
//    }


//    //---paint background rect---
//    painter->setBrush(backgroundColor);
//    painter->setPen( Qt::NoPen );
//    painter->drawRect( boundingRect() );//drawRoundedRect(boundingRect(), .25*boundingRect().height(), .25*boundingRect().height() );
}


//bool drawForward = ( isPosStrand && parent->minNT < parent->maxNT ) || ( !isPosStrand && parent->minNT > parent->maxNT );

//if( drawForward )//isPosStrand )//draw arrow to the right if positive strand
//{
//    float triangleDiff =  -bRect.width()*.25;//how far to draw rect before hitting triangle
//    if( bRect.width()*.25 < 4 )
//        triangleDiff = -(bRect.width());//if small gene- just draw triangle
//    if( bRect.width() > 12 )
//        triangleDiff = -12.0;

//    bRect.adjust(0, .25*bRect.height(), triangleDiff, -.25*bRect.height()); //adjust bounding rect
//    painter->drawRect(bRect);

//    //draw triangle
//    bRect = boundingRect();
//    QPointF p1( bRect.x()+bRect.width(), bRect.y()+bRect.height()/2 );
//    QPointF p2( bRect.x()+bRect.width()+triangleDiff, bRect.y());
//    QPointF p3( bRect.x()+bRect.width()+triangleDiff, bRect.y()+bRect.height() );
//    QPolygonF poly;
//    poly << p1 << p2 << p3;
//    painter->drawPolygon(poly);

//    //drawArrow( bRect, p1, p2, p3, colorToDraw );
//}
//else //draw arrow to the left
//{
//    float triangleDiff =  bRect.width()*.25;
//    if( bRect.width()*.25 < 4 )
//        triangleDiff = (bRect.width());
//    if( bRect.width() > 12 )
//        triangleDiff = 12.0;

//    bRect.adjust(triangleDiff, .25*bRect.height(), 0, -.25*bRect.height());
//    painter->drawRect(bRect);

//    bRect = boundingRect();
//    QPointF p1( bRect.x(), bRect.y()+bRect.height()/2 );
//    QPointF p2( bRect.x()+triangleDiff, bRect.y());
//    QPointF p3( bRect.x()+triangleDiff, bRect.y()+bRect.height() );
//    QPolygonF poly;
//    poly << p1 << p2 << p3;
//    painter->drawPolygon(poly);
//}

////make sure gene label drawn in the correct orientation
////ok, this kinda sucks ... because not very easy to translate to different situations
////but, I'll have to come up with something better later
//QPointF contigPos = parentItem()->parentItem()->pos();//parent contigs position in the browser
//QRectF browserBoundingRect = parentItem()->parentItem()->parentItem()->boundingRect();
//if( contigPos.y() < browserBoundingRect.y()+geneInfo->boundingRect().height())//50 )
//{
//    geneInfo->changeDirection( 2 );
//    geneInfo->setPos( (boundingRect().x() + width)/2.0, boundingRect().y()+boundingRect().height() );
//}
//else
//{
//   geneInfo->changeDirection( 0 );
//   geneInfo->setPos( (boundingRect().x()+width)/2.0, boundingRect().y() );

//}
